const Proxy = require('../../proxy')
const webDataUtil = require('../../util/webDataUtil')
const reqlib = require('app-root-path').require
const btbAs = reqlib('/app/common/btb')
const appConfig = reqlib('/config/index')
const commonDataCreate = require('./utils/commonDataCreate')
const AnalysisInfoFlag = reqlib('/app/analysis/analysisInfoFlag')
const AccountMachine = reqlib('/app/analysis/accountMachine')
const BuySellMachine = reqlib('/app/analysis/buySellMachine')
const Performance = reqlib('/app/common/performance')

const BtbIndexProxy = Proxy.BtbIndex
const DictionaryProxy = Proxy.Dictionary
const QuoteVolumeProxy = Proxy.QuoteVolume

function parseValue (val) {
  return parseFloat(val || 0) || 0
}

const changeMarginType = async (symbol) => {
  try {
    await webDataUtil.binance('futuresMarginType', {
      symbol: symbol,
      marginType: 'ISOLATED'
    })
  } catch (e) {
  }
}

const getWithKlineList = async (item) => {
  const key = item.code
  const klineResult = await Promise.all([
    webDataUtil.getFuturesKLinesAll({
      name: `${key}`,
      interval: '15m',
      count: 500
    }),
    webDataUtil.getFuturesKLinesAll({
      name: `${key}`,
      interval: '1h',
      count: 500
    }),
    webDataUtil.getFuturesKLinesAll({
      name: `${key}`,
      interval: '4h',
      count: 500
    }),
    webDataUtil.getFuturesKLinesAll({
      name: `${key}`,
      interval: '1d',
      count: 500
    })
  ])
  item.symbol = `${item.code}USDT`
  item.min_qty = item.f_min_qty
  item.max_qty = item.f_max_qty
  item.step_size = item.f_step_size
  item.min_notional = item.f_min_notional
  item.tick_size = item.f_tick_size
  item.klineList15m = klineResult[0]
  item.klineList1h = klineResult[1]
  item.klineList4h = klineResult[2]
  item.klineList1d = klineResult[3]
}

const analysisBtbItem = (item) => {
// 慢是因为分析2000天的，10个就20000天
  item.analysisInfoList15m = btbAs.getItemInfoList(item.klineList15m)
  item.analysisInfoList1h = btbAs.getItemInfoList(item.klineList1h)
  item.analysisInfoList4h = btbAs.getItemInfoList(item.klineList4h)
  item.analysisInfoList1d = btbAs.getItemInfoList(item.klineList1d)
  // 都转为从近到远
  item.analysisInfoList15m.reverse()
  item.analysisInfoList1h.reverse()
  item.analysisInfoList4h.reverse()
  item.analysisInfoList1d.reverse()
  item.analysisInfoList15m.forEach((v, index) => {
    commonDataCreate.asInfoExtend(item.analysisInfoList15m, v, index)
  })
  item.analysisInfoList1h.forEach((v, index) => {
    commonDataCreate.asInfoExtend(item.analysisInfoList1h, v, index)
  })
  item.analysisInfoList4h.forEach((v, index) => {
    commonDataCreate.asInfoExtend(item.analysisInfoList4h, v, index)
  })
  item.analysisInfoList1d.forEach((v, index) => {
    commonDataCreate.asInfoExtend(item.analysisInfoList1d, v, index)
  })
  item.klineList15m.reverse()
  item.klineList1h.reverse()
  item.klineList4h.reverse()
  item.klineList1d.reverse()
  item.analysisInfoFlag15m = new AnalysisInfoFlag({
    analysisInfoList: item.analysisInfoList15m,
    type: '15m',
    code: item.code
  })
  item.close = item.analysisInfoFlag15m.nowEmaInfo.close
  item.analysisInfoFlag1h = new AnalysisInfoFlag({
    analysisInfoList: item.analysisInfoList1h,
    type: '1h',
    code: item.code
  })
  item.analysisInfoFlag4h = new AnalysisInfoFlag({
    analysisInfoList: item.analysisInfoList4h,
    type: '4h',
    code: item.code
  })
  item.analysisInfoFlag1d = new AnalysisInfoFlag({
    analysisInfoList: item.analysisInfoList1d,
    type: '1d',
    code: item.code
  })
}

const getMarketBtbList = async (fsymbols) => {
  const futuresDailyRes = await webDataUtil.binance('getFuturesDaily')
  const futuresDailyData = futuresDailyRes.data || {}
  const list = []
  for (let key in futuresDailyData) {
    if (
      key.indexOf('USDT') !== -1 &&
      key.indexOf('USDT') !== 0 &&
      key.endsWith('USDT')
    ) {
      if (
        key.indexOf('DOWN') === -1 &&
        ['ETHUSDT', 'BTCUSDT'].indexOf(key) === -1
      ) {
        const itemData = futuresDailyData[key]
        const code = itemData.symbol.replace('USDT', '')
        itemData.code = code
        const fexInfo = btbAs.getEXInfo(fsymbols, code)
        if (fexInfo.status === 'TRADING') {
          list.push(itemData)
        }
      }
    }
  }
  return list
}

const getRecentMarketInfo = async () => {
  const list = await QuoteVolumeProxy.find({}, {
    skip: 0,
    limit: 200,
    sort: '-time'
  }) || []
  return list.map((v) => {
    const detail = v.detail || {}
    return {
      ...detail
    }
  })
}

const getBtbItemMarketInfo = (code, map) => {
  const info = map[code] || {}
  return {
    quote_volume: (parseFloat(info.quoteVolume) / 100000000) || 0,
    price_change_percent: parseFloat(info.priceChangePercent) || 0,
    price_change_rank: info.priceChangeRank || 0
  }
}

const getBtbIndexSort = (a, b, canOpenListRawMap) => {
  const aq = parseFloat(a.quoteVolume)
  const bq = parseFloat(b.quoteVolume)
  const openInfoa = canOpenListRawMap[a.code] || {}
  const openInfob = canOpenListRawMap[b.code] || {}
  openInfoa.buySellPriority = openInfoa.buySellPriority || 0
  openInfob.buySellPriority = openInfob.buySellPriority || 0
  if (openInfoa.buySellPriority !== openInfob.buySellPriority) {
    return openInfob.buySellPriority - openInfoa.buySellPriority
  }
  return bq - aq
}

const countPriceChangePercentRank = (list) => {
  const newList = [...list]
  // 大的在左边
  newList.sort((a, b) => {
    const aq = parseFloat(a.priceChangePercent)
    const bq = parseFloat(b.priceChangePercent)
    return bq - aq
  })
  newList.forEach((v, index) => {
    v.priceChangeRank = 0
    if (index >= 0 && index < 50) {
      v.priceChangeRank = index + 1
    }
    if (index >= (newList.length - 50) && index < (newList.length)) {
      v.priceChangeRank = index - newList.length
    }
  })
}

// 找币会找出10只，然后你交易基本上就只交易5只
module.exports = async function () {
  const p = new Performance()
  p.start()
  let btbLeverageRes = await DictionaryProxy.findOne({ key: 'btbLeverage' })
  let btbLeverage = parseInt((btbLeverageRes && btbLeverageRes.value) || 1) || 1
  const marketInfoList = await getRecentMarketInfo()
  let canOpenList = []
  let canOpenListRaw = []
  const preFind = await DictionaryProxy.findOne({
    key: 'preFindBtbResult'
  })
  let preFindBtbResult = {}
  if (preFind) {
    try {
      preFindBtbResult = JSON.parse(preFind.value)
      canOpenList = preFindBtbResult.canOpenList || []
      canOpenListRaw = preFindBtbResult.canOpenListRaw || []
    } catch (e) {
      preFindBtbResult = {}
      canOpenList = []
      canOpenListRaw = []
    }
  }
  const canOpenListRawMap = {}
  canOpenListRaw.forEach((v) => {
    canOpenListRawMap[v.code] = v
  })
  // 合约
  const fres = await webDataUtil.binance('getFuturesExchangeInfo')
  const fresData = fres.data || {}
  const fsymbols = fresData.symbols || []
  let btbList = await BtbIndexProxy.find({
    status: 1,
    hy_status: 1
  }, {
    sort: '-proportion',
    lean: true
  })
  btbList = btbList.filter((v) => {
    return !v.manual && !v.blacklist
  })
  const accountMachine = new AccountMachine({
    // 这边设置多少都没关系
    btbCount: 5,
    marketInfoList: marketInfoList || []
  })
  await accountMachine.refresh()
  let marketBtbList = await getMarketBtbList(fsymbols)
  countPriceChangePercentRank(marketBtbList)
  const marketBtbInfoMap = {}
  marketBtbList.forEach((item) => {
    marketBtbInfoMap[item.code] = item
  })
  // p.print('账户')
  const openList = []
  for (let i = 0; i < btbList.length; i++) {
    const item = btbList[i]
    const symbolAccount = accountMachine.getBtbItemAccount(`${item.code}USDT`) || {}
    const now_position_usdt = parseValue(item.now_position_usdt)
    // 把没有持仓的东西都关了
    if (Math.abs(symbolAccount.notional) < 3 && Math.abs(now_position_usdt) < 3) {
      await BtbIndexProxy.update({
        code: item.code
      }, {
        status: 0,
        hy_status: 0,
        ...btbAs.getClearIndexConfigParams()
      })
    } else {
      openList.push(item.code)
      await BtbIndexProxy.update({
        code: item.code
      }, {
        ...getBtbItemMarketInfo(item.code, marketBtbInfoMap)
      })
    }
    btbList[i] = null
  }
  marketBtbList = marketBtbList.filter((item) => {
    const fexInfo = btbAs.getEXInfo(fsymbols, item.code)
    const minNotional = (parseValue(fexInfo.notional) || 0)
    if (
      openList.indexOf(item.code) === -1 &&
      (
        (
          // 一千万
          (parseFloat(item.quoteVolume) / 100000000) > appConfig.btb.minQuoteVolume &&
          minNotional <= appConfig.btb.minNotionalLine
        ) ||
        canOpenList.indexOf(item.code) !== -1
      )
    ) {
      return true
    }
    return false
  })
  // 按信号和成交量排序
  marketBtbList.sort((a, b) => {
    // 返回1，a到b右边
    // 返回-1，b到a右边
    const openInfoa = canOpenListRawMap[a.code] || {}
    const openInfob = canOpenListRawMap[b.code] || {}
    openInfoa.buySellPriority = openInfoa.buySellPriority || 0
    openInfob.buySellPriority = openInfob.buySellPriority || 0
    const baseSort = getBtbIndexSort(a, b, canOpenListRawMap)
    // 先找出买卖信号的
    if (
      (openInfoa.buy || openInfoa.sell) &&
      (openInfob.buy || openInfob.sell)
    ) {
      return baseSort
    }
    if (openInfoa.buy || openInfoa.sell) {
      return -1
    }
    if (openInfob.buy || openInfob.sell) {
      return 1
    }
    // 都有
    if (
      canOpenList.indexOf(a.code) !== -1 &&
      canOpenList.indexOf(b.code) !== -1
    ) {
      return baseSort
    }
    if (canOpenList.indexOf(a.code) !== -1) {
      return -1
    }
    if (canOpenList.indexOf(b.code) !== -1) {
      return 1
    }
    return baseSort
  })
  p.print('市场')
  let count = 0
  for (let i = 0; i < marketBtbList.length; i++) {
    const item = marketBtbList[i]
    /**
     * 要杜绝占着茅坑不拉屎的仓位，入池多一点
     * （很奇怪，我一平仓，他就开始按预期涨跌了）
     * （写进备注。不要有这种想法）
     */
    if (
      openList.indexOf(item.code) === -1 &&
      openList.length < appConfig.btb.maxBtbNumber &&
      count < (appConfig.btb.maxBtbNumber * 2)
    ) {
      count++
      try {
        await getWithKlineList(item)
        analysisBtbItem(item)
        item.buySellMachine = new BuySellMachine({
          realBuySell: false,
          btbItem: item,
          futuresAccount: {},
          symbolAccount: {},
          marketInfo: preFindBtbResult,
          marketInfoList: marketInfoList || []
        })
        // 执行
        item.buySellMachine.execute()
        if (item.buySellMachine.getIfAddInPollResult()) {
          // if (item.buySellMachine.marketFlagInfo) {
          //   if (
          //     item.buySellMachine.marketFlagInfo.ifMarket15mAllGood3Days ||
          //     item.buySellMachine.marketFlagInfo.ifMarket15mAllBad3Days
          //   ) {
          //     if (btbLeverage < 6) {
          //       btbLeverage = 6
          //     }
          //   }
          // }
          let oldItem = await BtbIndexProxy.findOne({
            code: item.code
          })
          if (oldItem) {
            // 不能是手动的
            if (!oldItem.manual && !oldItem.blacklist) {
              await BtbIndexProxy.update({
                code: item.code
              }, {
                status: 1,
                hy_status: 1,
                leverage: btbLeverage,
                ...btbAs.getClearIndexConfigParams(),
                ...getBtbItemMarketInfo(item.code, marketBtbInfoMap)
              })
              await changeMarginType(`${item.code}USDT`)
              openList.push(item.code)
            }
          } else {
            const fexInfo = btbAs.getEXInfo(fsymbols, item.code)
            const exInfo = {}
            const minP = ((parseValue(fexInfo.minQty) || 0) * item.close) || 0
            const minNotional = (parseValue(fexInfo.notional) || 0) > minP ? (parseValue(fexInfo.notional) || 0) : minP
            if (fexInfo.maxQty && minNotional <= appConfig.btb.minNotionalLine) {
              const updateData = {
                name: item.code,
                code: item.code,
                proportion: 1,
                frequency: 'H',
                status: 1,
                buy_status: 1,
                sell_status: 1,
                condition_buy_status: 0,
                condition_sell_status: 0,
                condition_buy_number: 1,
                condition_sell_number: 1,
                strategy: '4',
                xh_status: 0,
                hy_status: 1,
                manual: 0,
                leverage: btbLeverage,
                min_qty: parseValue(exInfo.minQty) || 0,
                max_qty: parseValue(exInfo.maxQty) || 0,
                step_size: parseValue(exInfo.stepSize) || 0,
                min_notional: parseValue(exInfo.minNotional) || 0,
                tick_size: parseValue(exInfo.tickSize) || 0,
                f_min_qty: parseValue(fexInfo.minQty) || 0,
                f_max_qty: parseValue(fexInfo.maxQty) || 0,
                f_step_size: parseValue(fexInfo.stepSize) || 0,
                f_min_notional: parseValue(fexInfo.notional) || 0,
                f_tick_size: parseValue(fexInfo.tickSize) || 0,
                ...getBtbItemMarketInfo(item.code, marketBtbInfoMap)
              }
              await BtbIndexProxy.newAndSave(updateData)
              await changeMarginType(`${item.code}USDT`)
              openList.push(item.code)
            }
          }
        }
      } catch (e) {
      }
      marketBtbList[i] = null
    }
  }
  p.print('结束12')
}
